#include "stdafx.h"
#include "eventscb.h"
#include "out.h"

extern IDebugDataSpaces4* g_Data;
extern IDebugRegisters2* g_Registers;

//----------------------------------------------------------------------------
//
// Event callbacks.
//
//----------------------------------------------------------------------------


STDMETHODIMP_(ULONG)
EventCallbacks::AddRef(
					   THIS
					   )
{
	// This class is designed to be static so
	// there's no true refcount.
	return 1;
}

STDMETHODIMP_(ULONG)
EventCallbacks::Release(
						THIS
						)
{
	// This class is designed to be static so
	// there's no true refcount.
	return 0;
}

STDMETHODIMP
EventCallbacks::GetInterestMask(
								THIS_
								OUT PULONG Mask
								)
{
	*Mask =
		DEBUG_EVENT_BREAKPOINT |
		DEBUG_EVENT_EXCEPTION |
		DEBUG_EVENT_CREATE_PROCESS |
		DEBUG_EVENT_LOAD_MODULE |
		DEBUG_EVENT_SESSION_STATUS;

	//Print( "GetInterestMask 0x%x\n", Mask );

	return S_OK;
}

STDMETHODIMP
EventCallbacks::Breakpoint(
						   THIS_
						   IN PDEBUG_BREAKPOINT Bp
						   )
{
	ULONG Id;
	//Print( "Breakpoint\n" );

	if ( S_OK != Bp->GetId(&Id) )
		return DEBUG_STATUS_BREAK;

	//Print( "Breakpoint id 0x%x\n", Id );

	return DEBUG_STATUS_BREAK;
	//  return DEBUG_STATUS_NO_CHANGE;
	//    return DEBUG_STATUS_GO;
}

STDMETHODIMP
EventCallbacks::Exception(
						  THIS_
						  IN PEXCEPTION_RECORD64 Exception,
						  IN ULONG FirstChance
						  )
{
	// We want to handle these exceptions on the first
	// chance to make it look like no exception ever
	// happened.  Handling them on the second chance would
	// allow an exception handler somewhere in the app
	// to be hit on the first chance.
	//Print( "Exception\n" );

	if ( !FirstChance )
		return DEBUG_STATUS_NO_CHANGE;

	//
	// Check and see if the instruction causing the exception
	// is a cli or sti.  These are not allowed in user-mode
	// programs on NT so if they're present just nop them.
	//

	// sti/cli will generate privileged instruction faults.
	if (Exception->ExceptionCode != STATUS_PRIVILEGED_INSTRUCTION)
		return DEBUG_STATUS_NO_CHANGE;

	UCHAR Instr;
	ULONG Done;

	// It's a privileged instruction, so check the code for sti/cli.
	if (g_Data->ReadVirtual(Exception->ExceptionAddress, &Instr,
		sizeof(Instr), &Done) != S_OK ||
		Done != sizeof(Instr) ||
		(Instr != 0xfb && Instr != 0xfa))
	{
		return DEBUG_STATUS_NO_CHANGE;
	}

	// It's a sti/cli, so nop it out and continue.
	Instr = 0x90;
	if (g_Data->WriteVirtual(Exception->ExceptionAddress, &Instr,
		sizeof(Instr), &Done) != S_OK ||
		Done != sizeof(Instr))
	{
		return DEBUG_STATUS_NO_CHANGE;
	}

	// Fixed.
	//Print("Removed sti/cli at %I64x\n", Exception->ExceptionAddress);

	return DEBUG_STATUS_GO_HANDLED;
}

STDMETHODIMP
EventCallbacks::CreateProcess(
							  THIS_
							  IN ULONG64 ImageFileHandle,
							  IN ULONG64 Handle,
							  IN ULONG64 BaseOffset,
							  IN ULONG ModuleSize,
							  IN PCSTR ModuleName,
							  IN PCSTR ImageName,
							  IN ULONG CheckSum,
							  IN ULONG TimeDateStamp,
							  IN ULONG64 InitialThreadHandle,
							  IN ULONG64 ThreadDataOffset,
							  IN ULONG64 StartOffset
							  )
{
	UNREFERENCED_PARAMETER(ImageFileHandle);
	UNREFERENCED_PARAMETER(Handle);
	UNREFERENCED_PARAMETER(ModuleSize);
	UNREFERENCED_PARAMETER(CheckSum);
	UNREFERENCED_PARAMETER(TimeDateStamp);
	UNREFERENCED_PARAMETER(InitialThreadHandle);
	UNREFERENCED_PARAMETER(ThreadDataOffset);
	UNREFERENCED_PARAMETER(StartOffset);
	UNREFERENCED_PARAMETER(BaseOffset);
	UNREFERENCED_PARAMETER(ImageName);
	UNREFERENCED_PARAMETER(ModuleName);


	//Print( "start process: module '%s', image '%s'\n", ModuleName, ImageName );

	// The process is now available for manipulation.
	// Perform any initial code patches on the executable.
	//ApplyExePatches(ImageName, BaseOffset);

	// If the user requested that version calls be fixed up
	// register breakpoints to do so.
	//if (g_NeedVersionBps)
	//{
	//	AddVersionBps();
	//}

	return DEBUG_STATUS_GO;
}

STDMETHODIMP
EventCallbacks::LoadModule(
						   THIS_
						   IN ULONG64 ImageFileHandle,
						   IN ULONG64 BaseOffset,
						   IN ULONG ModuleSize,
						   IN PCSTR ModuleName,
						   IN PCSTR ImageName,
						   IN ULONG CheckSum,
						   IN ULONG TimeDateStamp
						   )
{
	UNREFERENCED_PARAMETER(ImageFileHandle);
	UNREFERENCED_PARAMETER(ModuleSize);
	UNREFERENCED_PARAMETER(ModuleName);
	UNREFERENCED_PARAMETER(CheckSum);
	UNREFERENCED_PARAMETER(TimeDateStamp);
	UNREFERENCED_PARAMETER(ImageName);
	UNREFERENCED_PARAMETER(BaseOffset);

	//Print( "load module: '%s', image '%s'\n", ModuleName, ImageName );
	//ApplyDllPatches(ImageName, BaseOffset);
	return DEBUG_STATUS_GO;
}

STDMETHODIMP
EventCallbacks::SessionStatus(
							  THIS_
							  IN ULONG SessionStatus
							  )
{
	// A session isn't fully active until WaitForEvent
	// has been called and has processed the initial
	// debug events.  We need to wait for activation
	// before we query information about the session
	// as not all information is available until the
	// session is fully active.  We could put these
	// queries into CreateProcess as that happens
	// early and when the session is fully active, but
	// for example purposes we'll wait for an
	// active SessionStatus callback.
	// In non-callback applications this work can just
	// be done after the first successful WaitForEvent.

	//Print( "SessionStatus 0x%x\n", SessionStatus );

	if (SessionStatus != DEBUG_SESSION_ACTIVE)
	{
		return S_OK;
	}

	HRESULT Status;

	//
	// Find the register index for eax as we'll need
	// to access eax.
	//
	ULONG EaxIndex = DEBUG_ANY_ID;

	Status = g_Registers->GetIndexByName("eax", &EaxIndex);

	//Print( "get eax status 0x%x (index 0x%x)\n", Status, EaxIndex );

	return S_OK;
}

STDMETHODIMP
EventCallbacks::ChangeDebuggeeState(
									THIS_
									__in ULONG Flags,
									__in ULONG64 Argument
									)
{
	UNREFERENCED_PARAMETER(Flags);
	UNREFERENCED_PARAMETER(Argument);

	//Print( "ChangeDebuggeeState: flags  0x%x", Flags );

	return S_OK;
}
STDMETHODIMP
EventCallbacks::ChangeEngineState(
								  THIS_
								  __in ULONG Flags,
								  __in ULONG64 Argument
								  )
{
	UNREFERENCED_PARAMETER(Flags);
	UNREFERENCED_PARAMETER(Argument);

	//Print( "ChangeEngineState: flags 0x%x", Flags );

	return S_OK;
}
